from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
from math import pi, cos, sin
# import cv2
# from glob import glob


blueStyle = 'background-color:#3daee9;border-width:1px'
darkStyle = 'background-color:#31363b;border-width:1px'


class Label(QLabel):
    def __init__(self, text):
        super(Label, self).__init__()
        self.setText(text)
        self.setAlignment(Qt.AlignCenter)


class Slider(QSlider):
    currentValue = Signal(int)

    def __init__(self, min, max):
        super(Slider, self).__init__()
        self.setOrientation(Qt.Horizontal)
        self.min, self.max = min, max
        self.setMinimum(min)
        self.setMaximum(max)

    def mousePressEvent(self, event):
        self.changeValue(event)

    def mouseMoveEvent(self, event):
        self.changeValue(event)

    def changeValue(self, event):
        x = event.x()
        if x < 0:
            x = 0
        if x > self.width():
            x = self.width()
        value = int(self.min + x / self.width() * (self.max - self.min))
        self.setValue(value)
        self.currentValue.emit(value)

    def wheelEvent(self, event):
        pass


class OptionWidget(QWidget):  # 调节页
    mirrored = Signal()

    def __init__(self):
        super(OptionWidget, self).__init__()
        self.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.setWindowTitle('透明度和图片缩放')
        self.setFixedSize(500, 175)
        layout = QGridLayout(self)
        layout.addWidget(Label('透明'), 0, 0, 1, 1)
        self.opacitySlider = Slider(10, 100)
        self.opacitySlider.setValue(100)
        layout.addWidget(self.opacitySlider, 0, 1, 1, 5)
        self.percentLabel = Label('100%')
        layout.addWidget(self.percentLabel, 0, 6, 1, 1)
        layout.addWidget(Label('缩放'), 1, 0, 1, 1)
        self.scaleSlider = Slider(0, 34)
        self.scaleSlider.setTickInterval(1)
        # self.scaleSlider.setTickPosition(QSlider.TicksAbove)
        layout.addWidget(self.scaleSlider, 1, 1, 1, 5)
        self.scaleLabel = Label('x0.04')
        layout.addWidget(self.scaleLabel, 1, 6, 1, 1)
        layout.addWidget(Label('旋转'), 2, 0, 1, 1)
        self.rotateSlider = Slider(-180, 180)
        self.rotateSlider.setTickInterval(1)
        self.rotateSlider.currentValue.connect(self.setRotateLabel)
        layout.addWidget(self.rotateSlider, 2, 1, 1, 5)
        self.rotateLabel = Label('0 度')
        layout.addWidget(self.rotateLabel, 2, 6, 1, 1)
        self.mirrorToken = False
        self.mirrorButton = QPushButton('镜像')
        self.mirrorButton.clicked.connect(self.setMirrored)
        layout.addWidget(self.mirrorButton, 3, 0, 1, 1)

    def setRotateLabel(self, value):
        self.rotateLabel.setText('%s 度' % value)

    def setMirrored(self):
        self.mirrorToken = not self.mirrorToken
        if self.mirrorToken:
            self.mirrorButton.setStyleSheet(blueStyle)
        else:
            self.mirrorButton.setStyleSheet(darkStyle)
        self.mirrored.emit()


class ImageWidget(QLabel):  # 画布控件 自定义QLabel实现
    resizeSignal = Signal(QSize)  # 画框大小变化信号
    openSignal = Signal()  # 打开图片选择窗信号
    moveSignal = Signal(QPoint)  # 移动全局坐标偏移量
    rightClickSignal = Signal(QPoint)  # 右键点击
    leftClickSignal = Signal()  # 左键点击
    scaleSignal = Signal(list)  # 缩放信号
    mirrorSignal = Signal()  # 镜像信号

    def __init__(self, parent):
        super(ImageWidget, self).__init__(parent)
        # self.setFrameShape(QFrame.Box)  # 本来想给画布加上边框 效果不好看取消了
        # self.setStyleSheet('border:3px dot-dot-dash #cfcfd0')  # dashed|dot-dash|dot-dot-dash|dotted|double|solid
        self.image = None
        self.scaleRates = [i / 100 for i in range(4, 20)] + [i / 10 for i in range(2, 21)]
        self.rotateAngle = 0
        self.sin = 0  # sin值
        self.cos = 1  # cos值
        self.rightButtonPressed, self.leftButtonPressed = False, False
        self.point = QPoint(0, 0)
        self.scaledImage = QPixmap()
        self.pressTimeoutCount = 0
        self.pressTimer = QTimer()
        self.pressTimer.timeout.connect(self.longPress)

    def paintEvent(self, event):  # 重绘事件 响应画布拖拽 旋转等操作
        painter = QPainter()
        painter.setRenderHint(QPainter.SmoothPixmapTransform, QPainter.Antialiasing)
        painter.begin(self)
        if self.rotateAngle:  # 判断旋转
            painter.translate(self.width() / 2, self.height() / 2)
            painter.rotate(self.rotateAngle)
            painter.translate(-self.width() / 2, -self.height() / 2)
        painter.drawPixmap(self.point, self.scaledImage)  # 根据偏移顶点重新绘制scaledImage
        painter.end()

    def setImagePath(self, image='', width=400, initMirror=True):  # 设置原始图片 以及初始化scaledImage对象
        self.rotateAngle = 0
        if image:  # 原始图片有效 则加载进self.image（QPixmap对象）
            if type(image) == str:  # 路径导入
                self.image = QPixmap(image)
            elif type(image) == QPixmap:  # 剪切板粘贴
                self.image = image
        if self.image.width():  # 判断导入图片是否有宽度值
            for self.scaleIndex, scaleRate in enumerate(self.scaleRates):  # 计算合适的缩放比率并发射信号给主进程设置缩放页的滑条
                if self.image.width() * scaleRate >= width:
                    self.scaleSignal.emit([self.scaleIndex, scaleRate])
                    break
            self.scaledImage = self.image.scaled(self.image.width() * scaleRate, self.image.height() * scaleRate)  # 缩放
            self.resizeSignal.emit(self.scaledImage.size())
            if initMirror:
                self.mirrorSignal.emit()
            self.point = QPoint(0, 0)  # 初始化顶点坐标
            self.repaint()  # 重绘

    def mousePressEvent(self, event):  # 鼠标点击事件 要判断左键和右键 功能不同
        self.mouseMove = False
        self.startPos = event.pos()
        if event.button() == Qt.RightButton:  # 右键标志位 之后要判断是拖拽还是右键菜单
            self.setCursor(Qt.ClosedHandCursor)  # 抓取图标
            self.rightButtonPressed = True
        else:  # 左键
            if not self.image:  # 没加载图片的时候发射信号弹出图片选择窗
                self.openSignal.emit()
            else:  # 左键标志位 开始长按计时器
                self.setCursor(Qt.SizeAllCursor)  # 移动图标
                self.longPressPoint = event.pos()
                self.pressTimeoutCount = 0  # 重置长按计数
                self.pressTimer.start(750)  # 定时器间隔
                self.leftButtonPressed = True

    def longPress(self):  # 长按计时器触发 用于数位板适配
        self.pressTimeoutCount += 1
        if self.pressTimeoutCount == 1:  # 长按超时第1次变为拖拽画布
            self.setCursor(Qt.ClosedHandCursor)
            self.rightButtonPressed = True
            self.leftButtonPressed = False
        elif self.pressTimeoutCount == 2:  # 长按超时第2次改为弹出菜单
            self.pressTimer.stop()
            self.rightClickSignal.emit(self.longPressPoint)

    def mouseReleaseEvent(self, event):  # 鼠标松开 要判断左键还是右键 功能不同
        self.setCursor(Qt.ArrowCursor)
        self.pressTimer.stop()  # 停止长按计时
        self.rightButtonPressed = False
        self.leftButtonPressed = False
        if event.button() == Qt.RightButton:
            if not self.mouseMove:  # 原地松开 发射信号弹出右键菜单
                self.rightClickSignal.emit(event.pos())
        else:
            if not self.mouseMove:
                self.leftClickSignal.emit()

    def mouseMoveEvent(self, event):  # 鼠标移动事件 要判断左键还是右键 功能不同
        self.pressTimer.stop()
        self.mouseMove = True
        if self.rightButtonPressed:  # 右键按住拖动画布
            if self.image:
                if self.rotateAngle:
                    newX, newY = event.pos().x(), event.pos().y()
                    oldX, oldY = self.startPos.x(), self.startPos.y()
                    deltaX = newX - oldX
                    deltaY = newY - oldY
                    newDeltaX = deltaY * self.sin + deltaX * self.cos
                    newDeltaY = deltaY * self.cos - deltaX * self.sin
                    newPoint = self.startPos + QPoint(newDeltaX, newDeltaY)
                else:
                    newPoint = event.pos()
                moveDelta = newPoint - self.startPos  # 计算偏移量
                point = self.point + moveDelta
                imageX, imageY = point.x(), point.y()
                imageW, imageH = self.scaledImage.width(), self.scaledImage.height()
                parentW, parentH = self.parent().width(), self.parent().height()
                if imageX + imageW < 5 or imageY + imageH < 5 or imageX > parentW - 5 or imageY > parentH - 25:
                    pass
                else:
                    self.point = point
                    self.startPos = event.pos()
                    self.repaint()
        elif self.leftButtonPressed:  # 左键按住发射位移量给主进程移动全局坐标
            moveDelta = event.pos() - self.startPos  # 计算偏移量
            self.moveSignal.emit(moveDelta)

    def wheelEvent(self, event):  # 滚轮事件缩放画布 判断往上滚还是往下滚
        if self.image:
            eventX, eventY = event.x(), event.y()  # 获取鼠标缩放坐标
            pointX, pointY = self.point.x(), self.point.y()
            oldWidth, oldHeight = self.scaledImage.width(), self.scaledImage.height()
            if pointX <= eventX <= pointX + oldWidth and pointY <= eventY <= pointY + oldHeight:  # 判断鼠标在画布内
                if event.angleDelta().y() < 0 < self.scaleIndex:  # 防止越界
                    self.scaleIndex -= 1
                elif event.angleDelta().y() > 0 and self.scaleIndex < len(self.scaleRates) - 1:  # 防止越界
                    self.scaleIndex += 1
                scaleRate = self.scaleRates[self.scaleIndex]  # 获取缩放比率
                self.scaledImage = self.image.scaled(self.image.width() * scaleRate, self.image.height() * scaleRate)
                newWidth, newHeight = self.scaledImage.width(), self.scaledImage.height()
                w = eventX - newWidth * (eventX - pointX) / oldWidth
                h = eventY - newHeight * (eventY - pointY) / oldHeight
                self.point = QPoint(w, h)  # 设置顶点坐标
                self.repaint()  # 重绘
                self.scaleSignal.emit([self.scaleIndex, scaleRate])  # 发射缩放倍数

    def sliderScale(self, index):  # 调节页滑条缩放
        if self.image:
            self.scaleIndex = index
            scaleRate = self.scaleRates[self.scaleIndex]
            self.scaledImage = self.image.scaled(self.image.width() * scaleRate, self.image.height() * scaleRate)
            newWidth, newHeight = self.scaledImage.width(), self.scaledImage.height()
            self.point = QPoint((self.width() - newWidth) / 2, (self.height() - newHeight) / 2)
            self.repaint()  # 重绘
            # self.scaleSignal.emit([self.scaleIndex, scaleRate])

    def rotate(self, value):  # 调节页滑条旋转
        if self.image:
            self.rotateAngle = value  # 修改旋转度数
            theta = self.rotateAngle / 180 * pi
            self.sin = sin(theta)  # 更新旋转度数
            self.cos = cos(theta)  # 更新旋转度数
            self.repaint()  # 重绘

    def mirror(self):
        if self.image:  # 图片有效
            if self.image.width():  # 图片有宽度
                tempImage = self.image.toImage()
                tempImage = tempImage.mirrored(True, False)  # 镜像
                self.image = QPixmap.fromImage(tempImage)  # 修改原始图像
                scaleRate = self.scaleRates[self.scaleIndex]  # 获取缩放比率
                self.scaledImage = self.image.scaled(self.image.width() * scaleRate, self.image.height() * scaleRate)
                self.repaint()
